﻿

using Bentley.DgnPlatformNET;
using Bentley.GeometryNET;
using System.Runtime.InteropServices;
namespace CheccCoverplateCulvertAssemble.Conmmon
{
    public class CulvertHelpers
    {
        public static SolidKernelEntity CreateBodyFromSweep(CurveVector pathCur, CurveVector secCur, DgnModel modelRef)
        {
            pathCur.GetStartEnd(out DPoint3d startPt, out _, out DVector3d startTangent, out _);
            CurveVector secCur1 = secCur;
            TransformSectionNormaltoSolidPath(ref secCur1, startPt, startTangent);
            BCIMNETGS.SolidUtil.Create.BodyFromSweep(out SolidKernelEntity entity, secCur, pathCur, modelRef, false, true, false, null, null, null, null);
            return entity;
        }
        public static SolidKernelEntity CreateBodyFromExtrusion(CurveVector secCur, double extrusionLength, DPoint3d centroid, DVector3d extrudDir, DgnModel modelRef)
        {
            TransformSectionNormaltoSolidPath(ref secCur, centroid, extrudDir);
            BentleyStatus status = BCIMNETGS.SolidUtil.Create.BodyFromExtrusion(
                out SolidKernelEntity entity, secCur, extrusionLength, modelRef, extrudDir, false, null);
            return entity;
        }
        public static void TransformSectionNormaltoSolidPath(ref CurveVector sectionCurve, DPoint3d uorLocate, DVector3d pathDir)
        {
            DVector3d xAxis, yAxis, zAxis;
            zAxis = pathDir;

            zAxis.NormalizeInPlace();
            yAxis = DVector3d.FromXYZ(0, 0, -1);
            if (zAxis.IsParallelOrOppositeTo(yAxis))
            {
                yAxis = DVector3d.FromXYZ(0, 1, 0);
            }
            xAxis = yAxis.CrossProduct(zAxis);
            xAxis.NormalizeInPlace();

            yAxis = xAxis.CrossProduct(zAxis);
            yAxis.NormalizeInPlace();

            DTransform3d trans = DTransform3d.FromOriginAndColumnPoints(uorLocate, xAxis, yAxis, zAxis);

            sectionCurve.Transform(trans);
        }
        public static void RoateCurveVectorByYaxis(ref CurveVector sectionCurve, double angle)
        {
            DMatrix3d roateMatrix = DMatrix3d.Rotation(new DVector3d(0, 1, 0), new Angle() { Degrees = angle });
            DTransform3d roateTransform = new DTransform3d() { Matrix = roateMatrix };
            sectionCurve.Transform(roateTransform);
        }

        public static T TransformElementNormaltoPath<T>(T element, DPoint3d uorLocate, DVector3d pathDir) where T : notnull, Element
        {
            DVector3d xAxis, yAxis, zAxis;
            zAxis = pathDir;

            zAxis.NormalizeInPlace();
            yAxis = DVector3d.FromXYZ(0, 0, -1);
            if (zAxis.IsParallelOrOppositeTo(yAxis))
            {
                yAxis = DVector3d.FromXYZ(0, 1, 0);
            }
            xAxis = yAxis.CrossProduct(zAxis);
            xAxis.NormalizeInPlace();

            yAxis = xAxis.CrossProduct(zAxis);
            yAxis.NormalizeInPlace();

            DTransform3d trans = DTransform3d.FromOriginAndColumnPoints(uorLocate, xAxis, yAxis, zAxis);
            TransformInfo roateTransinfo = new TransformInfo(trans);
            element.ApplyTransform(roateTransinfo);
            return element;
        }
        /// <summary>
        ///   按照给定角度 绕x轴正向旋转元素,正-逆时针,负-顺时针
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="element"></param>
        /// <param name="angle">旋转角度</param>
        /// <returns>旋转后的元素</returns>
        public static T TransformElementRoate90ByXaxis<T>(T element, double angle) where T : Element
        {
            DMatrix3d roateMatrix = DMatrix3d.Rotation(new DVector3d(1, 0, 0), new Angle() { Degrees = angle });
            DTransform3d roateTransform = new DTransform3d() { Matrix = roateMatrix };
            TransformInfo roateTransinfo = new TransformInfo(roateTransform);
            element.ApplyTransform(roateTransinfo);
            return element;
        }
        /// <summary>
        ///   按照给定角度 绕y轴正向旋转元素,正-逆时针,负-顺时针
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="element"></param>
        /// <param name="angle">旋转角度</param>
        /// <returns>旋转后的元素</returns>
        public static T TransformElementRoateByYaxis<T>(T element, double angle) where T : Element
        {

            DMatrix3d roateMatrix = DMatrix3d.Rotation(new DVector3d(0, 1, 0), new Angle() { Degrees = angle });
            DTransform3d roateTransform = new DTransform3d() { Matrix = roateMatrix };
            TransformInfo roateTransinfo = new TransformInfo(roateTransform);
            element.ApplyTransform(roateTransinfo);
            return element;
        }
        /// <summary>
        ///   按照给定角度 绕z轴正向旋转元素,正-逆时针,负-顺时针
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="element"></param>
        /// <param name="angle">旋转角度</param>
        /// <returns>旋转后的元素</returns>
        public static T TransformElementRoate90ByZaxis<T>(T element, double angle) where T : Element
        {

            DMatrix3d roateMatrix = DMatrix3d.Rotation(new DVector3d(0, 0, 1), new Angle() { Degrees = angle });
            DTransform3d roateTransform = new DTransform3d() { Matrix = roateMatrix };
            TransformInfo roateTransinfo = new TransformInfo(roateTransform);
            element.ApplyTransform(roateTransinfo);
            return element;
        }
        public static T GetMeterToUorElement<T>(T element) where T : Element
        {
            double uorPerMeter = Session.Instance.GetActiveDgnModel().GetModelInfo().UorPerMeter;
            DMatrix3d rMatrix = DMatrix3d.Identity * uorPerMeter;
            DTransform3d trans = new(rMatrix);
            TransformInfo transInfo = new(trans);
            element.ApplyTransform(transInfo);
            return element;
        }

        public static (List<Element> curveElements, DPoint3d? startPoint, DPoint3d? endPoint, DVector3d? startTangentVector, DVector3d? endTangentVector) GetElementsByStartAndEndFractionFromCurveVector(CurveVector curves, double backLength)
        {
            double d_length = backLength * 0.001 * GeneralHelpers.UorToMeters;
            List<Element> ret = new();
            if (curves.Count == 0)
            {
                return (ret, null, null, null, null);
            }
            if (curves.Count == 1)
            {
                double startFraction = d_length / curves.SumOfLengths();
                double endFraction = 1 - startFraction;
                CurvePrimitive partCurvePrimitive = CurvePrimitive.CreatePartialCurve(curves.GetPrimitive(0), startFraction, endFraction, 0);
                Element elementPartOfBaseLine = DraftingElementSchema.ToElement(Session.Instance.GetActiveDgnModel(), partCurvePrimitive, null);
                ret.Add(elementPartOfBaseLine);
                curves.GetPrimitive(0).FractionToPoint(startFraction, out DPoint3d startPoint1, out DVector3d startTangent, out DVector3d startDerivative2, out DVector3d startDerivative3);
                curves.GetPrimitive(0).FractionToPoint(endFraction, out DPoint3d endPoint1, out DVector3d endTangent, out DVector3d endDerivative2, out DVector3d endDerivative3);
                return (ret, startPoint1, endPoint1, startTangent, endTangent);
            }
            else
            {
                DPoint3d startPoint = DPoint3d.Zero;
                DPoint3d endPoint = DPoint3d.Zero;
                DVector3d startTangentVector = DVector3d.Zero;
                DVector3d endTangentVector = DVector3d.Zero;
                for (int i = 0; i < curves.Count; i++)
                {
                    CurvePrimitive curvePrimitive = curves.GetPrimitive(i);
                    curvePrimitive.Length(out double length);
                    if (0 == i)
                    {
                        double startFraction = d_length / length;
                        CurvePrimitive partCurvePrimitive = CurvePrimitive.CreatePartialCurve(curvePrimitive, startFraction, 1, 0);
                        Element elementPartOfBaseLine = DraftingElementSchema.ToElement(Session.Instance.GetActiveDgnModel(), partCurvePrimitive, null);
                        curvePrimitive.FractionToPoint(startFraction, out startPoint, out startTangentVector, out DVector3d startDerivative2, out DVector3d startDerivative3);
                        ret.Add(elementPartOfBaseLine);
                    }
                    else if (i == curves.Count - 1)
                    {
                        double endFraction = 1 - d_length / length;
                        CurvePrimitive partCurvePrimitive = CurvePrimitive.CreatePartialCurve(curves.GetPrimitive(i), 0, endFraction, 0);
                        Element elementPartOfBaseLine = DraftingElementSchema.ToElement(Session.Instance.GetActiveDgnModel(), partCurvePrimitive, null);
                        curvePrimitive.FractionToPoint(endFraction, out endPoint, out endTangentVector, out DVector3d startDerivative2, out DVector3d startDerivative3);
                        ret.Add(elementPartOfBaseLine);
                    }
                    else
                    {
                        Element elementPartOfBaseLine = DraftingElementSchema.ToElement(Session.Instance.GetActiveDgnModel(), curvePrimitive, null);
                        ret.Add(elementPartOfBaseLine);
                    }
                }
                return (ret, startPoint, endPoint, startTangentVector, endTangentVector);
            }
        }

        public static (CurvePrimitive firstCurvePrimitive, CurvePrimitive lastCurvePrimitive) GetFirstAndLastCurveFromCurveVector(CurveVector curves)
        {
            return (curves.GetPrimitive(0), curves.GetPrimitive(curves.Count - 1));
        }
        public static (int rebarCount, List<double> distanceList) GetRebarDistributedInfoByExpression(string expression)
        {
            int rebarCount = 1;
            List<double> distanceList = new();
            List<string> strList = Regex.Split(expression, @"\(|\)|\+").Where(s => s != "").ToList();
            foreach (string str in strList)
            {
                if (str.Contains("*"))
                {
                    string[] strings = Regex.Split(str, @"\*");
                    rebarCount += Convert.ToInt16(strings[0]);
                }
                else
                {
                    rebarCount += 1;
                }
            }

            double totalDistributedLength = 0.0;
            foreach (var item in strList)
            {
                if (item.Contains(@"*"))
                {
                    string[] strings = Regex.Split(item, @"\*");
                    totalDistributedLength += Convert.ToDouble(strings[0]) * Convert.ToDouble(strings[1]);
                }
                else
                {
                    totalDistributedLength += Convert.ToDouble(item);
                }
            }
            List<double> intervalList = new();
            intervalList.Add(0.0);
            foreach (var item in strList)
            {
                if (item.Contains(@"*"))
                {
                    string[] strings = Regex.Split(item, @"\*");
                    for (int i = 0; i < Convert.ToInt16(strings[0]); i++)
                    {
                        intervalList.Add(Convert.ToDouble(strings[1]));
                    }
                }
                else
                {
                    intervalList.Add(Convert.ToDouble(item));
                }
            }
            double halfTotalDistributedLength = -totalDistributedLength / 2.0;
            foreach (var item in intervalList)
            {
                halfTotalDistributedLength += item;
                distanceList.Add(halfTotalDistributedLength);
            }
            return (rebarCount, distanceList);
        }
        public static (DEllipse3d? dEllipse, DPoint3d? tangentPointOnLine1, DPoint3d? tangentPointOnLine2) CreateArcByLineToLine(DSegment3d line1, DSegment3d line2, double radius)
        {
            if (line1.UnitTangent.IsParallelOrOppositeTo(line2.UnitTangent))
            {
                return (null, null, null);
            }
            line1.UnitTangent.TryNormalize(out DVector3d tangentVector1);
            line2.UnitTangent.TryNormalize(out DVector3d tangentVector2);
            line1.UnitTangent.CrossProduct(line2.UnitTangent).TryNormalize(out DVector3d nomarlVector);
            nomarlVector.CrossProduct(line1.UnitTangent).TryNormalize(out DVector3d vectorLine1);
            nomarlVector.CrossProduct(line2.UnitTangent).TryNormalize(out DVector3d vectorLine2);
            var angle = Math.PI - Math.Acos(line1.UnitTangent.DotProduct(line2.UnitTangent));
            DSegment3d.IntersectXY(line1, line2, out double fraction1, out DPoint3d point1, out double fraction2, out DPoint3d point2);
            var startPoint = point1 - line1.UnitTangent * radius * 0.001 * GeneralHelpers.UorToMeters * (1.0 / Math.Tan(angle * 0.5));
            var endPoint = point2 + line2.UnitTangent * 0.001 * GeneralHelpers.UorToMeters * radius * (1.0 / Math.Tan(angle * 0.5));
            var centerPoint = startPoint + (vectorLine1 * radius * 0.001 * GeneralHelpers.UorToMeters);
            var midVector = new DVector3d(centerPoint, point1);
            midVector.TryNormalize(out DVector3d midNormalVector);
            var midPoint = centerPoint + midNormalVector * radius * 0.001 * GeneralHelpers.UorToMeters;
            DEllipse3d.TryCircularArcFromStartMiddleEnd(startPoint, midPoint, endPoint, out DEllipse3d ret);
            return (ret, startPoint, endPoint);
        }
    }
}
